package com.liuqi.knet.remoting.transport.netty;

import com.liuqi.knet.remoting.Channel;
import com.liuqi.knet.remoting.ChannelHandler;
import com.liuqi.knet.remoting.KNetConfig;
import com.liuqi.knet.util.NetUtil;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务端处理器
 *
 * @author liuqi
 * @date 2025/2/8 17:24
 **/
@io.netty.channel.ChannelHandler.Sharable
class NettyServerHandler extends ChannelDuplexHandler {

    /**
     * 保存所有客户端的连接
     * key: ip:port
     */
    private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>();

    private final ChannelHandler handler;

    private final KNetConfig kNetConfig;

    public NettyServerHandler(ChannelHandler handler, KNetConfig kNetConfig) {
        if (handler == null) {
            throw new IllegalArgumentException("handler == null");
        }
        if (kNetConfig == null) {
            throw new IllegalArgumentException("kNetConfig == null");
        }
        this.handler = handler;
        this.kNetConfig = kNetConfig;
    }

    public Map<String, Channel> getChannels() {
        return channels;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();

        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), handler, kNetConfig);
        try {
            if (channel != null) {
                channels.put(NetUtil.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
            }
            handler.connected(channel);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), handler, kNetConfig);
        try {
            channels.remove(NetUtil.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()));
            handler.disconnected(channel);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), handler, kNetConfig);
        try {
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        super.write(ctx, msg, promise);
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), handler, kNetConfig);
        try {
            handler.send(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), handler, kNetConfig);
        try {
            handler.caught(channel, cause);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

}
